feat: initial website design#2
Conversation
creates interface for user to code, and a submit button. Note: this is a work in progress.
* feat(web): send code to backend
Moved state logic to route component.
Added prettier as code formatter with style
Includes header, description, examples, and constraints components.
…efault. Add toggles to toggle visibility of chat UI or problem UI.
…ates to course list
replaces in ChatBox and Problem
* ci: github action for version bumping and preview.
* chore: linting files
* feat: protected route before login. There are still several authentication issues left that is WIP
🚀 Release PreviewIf you merge this PR right now, Based on the conventional commits found in this branch. |
|
@millecodex Can you review this? I would suggest adding coderabbit for AI reviewer on this repository https://www.coderabbit.ai/ as it provides free review. |
There was a problem hiding this comment.
Pull request overview
This PR introduces an initial full-stack “learning website” implementation: a TanStack Start (Vite/React) web app with Better Auth + Convex-backed invitation-code onboarding, plus a FastAPI backend endpoint that proxies student code execution to Judge0. It also adds release automation via git-cliff and GitHub Actions.
Changes:
- Added TanStack Start web app scaffolding (router, routes, UI components, Tailwind styling) including authenticated course/workspace UI.
- Added Convex schema + queries/mutations (courses/questions + invitation code redemption + user profile syncing).
- Added FastAPI server with
/executeendpoint calling Judge0, plus GitHub Actions workflows for release + PR version preview.
Reviewed changes
Copilot reviewed 64 out of 70 changed files in this pull request and generated 18 comments.
Show a summary per file
| File | Description |
|---|---|
| web/vite.config.ts | Vite/TanStack Start plugin configuration. |
| web/tsr.config.json | TanStack Router CLI configuration. |
| web/tsconfig.json | TypeScript compiler settings and path aliases. |
| web/test/studentcode.test.ts | Vitest test for /execute integration. |
| web/src/styles.css | Global Tailwind + theme styling. |
| web/src/routeTree.gen.ts | Generated TanStack Router route tree. |
| web/src/routes/auth.tsx | Auth page (sign-in/sign-up/magic-link + invitation modal). |
| web/src/routes/api/auth/$.ts | Better Auth API handlers route. |
| web/src/routes/_authenticated.tsx | Authenticated layout + invitation-code redemption sync. |
| web/src/routes/_authenticated.index.tsx | Authenticated home route (course list). |
| web/src/routes/_authenticated.course.tsx | Authenticated course workspace route (editor/problem/chat). |
| web/src/routes/__root.tsx | Root document shell + providers + devtools. |
| web/src/router.tsx | Router creation + SSR query integration. |
| web/src/lib/utils.ts | Utility cn() for class merging. |
| web/src/lib/auth.ts | Better Auth server configuration. |
| web/src/lib/auth.functions.ts | Server functions for session + user existence check. |
| web/src/lib/auth-client.ts | Better Auth client configuration. |
| web/src/integrations/tanstack-query/root-provider.tsx | QueryClient context creation. |
| web/src/integrations/tanstack-query/devtools.tsx | TanStack Query devtools panel plugin. |
| web/src/integrations/better-auth/header-user.tsx | Header user widget (session + sign out). |
| web/src/components/ui/button.tsx | Shared button component (CVA + Slot). |
| web/src/components/student/SidePane.tsx | Student chat side panel wrapper. |
| web/src/components/student/ResetCodeForm.tsx | Reset-code confirmation modal. |
| web/src/components/student/problem/Problem.tsx | Problem description panel (markdown). |
| web/src/components/student/problem/ChatBox.tsx | Chat UI (mocked assistant responses). |
| web/src/components/student/InteractionBar.tsx | Workspace toolbar (language/font/sidebar toggles). |
| web/src/components/student/CodeEditor.tsx | Monaco editor wrapper + console drawer + run/submit UI. |
| web/src/components/ide/Terminal.tsx | Execution result console UI. |
| web/src/components/home/SidePane.tsx | Home sidebar UI (profile/streak/progress/mock). |
| web/src/components/home/CourseList.tsx | Course/problem list sourced from Convex. |
| web/src/components/auth/InvitationCodeModal.tsx | Invitation code entry modal. |
| web/src/components/auth/forms/SignUpForm.tsx | Sign-up form with invitation-code validation + Convex sync. |
| web/src/components/auth/forms/SignInForm.tsx | Email/password sign-in form. |
| web/src/components/auth/forms/MagicLinkForm.tsx | Magic-link form with “user exists” check. |
| web/src/components/auth/AuthTextField.tsx | Auth text input with icons/errors + password reveal. |
| web/src/components/auth/AuthTabs.tsx | Auth segmented tabs UI (signin/magiclink/signup). |
| web/src/components/auth/AuthCard.tsx | Auth card container layout. |
| web/src/components/auth/AuthButton.tsx | Auth button component with variants/loading. |
| web/src/components/auth/AlreadySignedIn.tsx | “Already signed in” screen/actions. |
| web/README.md | Web app setup docs (TanStack Start template). |
| web/public/robots.txt | Robots file. |
| web/public/manifest.json | PWA manifest. |
| web/pnpm-workspace.yaml | pnpm workspace policy config. |
| web/package.json | Web dependencies/scripts. |
| web/convex/schema.ts | Convex schema (courses/questions/users/invites). |
| web/convex/invitationCodes.ts | Convex invitation code logic (validate/redeem/create user). |
| web/convex/courses.ts | Convex queries for questions/courses. |
| web/components.json | shadcn/ui config. |
| web/biome.json | Biome formatter/linter config. |
| web/.vscode/settings.json | VS Code settings (Biome defaults + generated file exclusions). |
| web/.prettierrc | Prettier config (repo still primarily uses Biome). |
| web/.gitignore | Web ignores (dist/env/generated). |
| web/.env.example | Web env example. |
| web/.cursorrules | Cursor/shadcn instructions. |
| web/.cta.json | TanStack Create App metadata. |
| server/README.md | Server readme (FastAPI). |
| server/pyproject.toml | Python project metadata + deps. |
| server/model/student_code.py | Pydantic model(s) for student code. |
| server/main.py | FastAPI app + CORS + /execute Judge0 proxy. |
| server/.python-version | Python version pin. |
| server/.env.example | Server env example for Judge0. |
| cliff.toml | git-cliff configuration. |
| .gitignore | Repo-wide ignores for env/venv/pycache. |
| .github/workflows/release.yaml | Automated release workflow using git-cliff. |
| .github/workflows/pr-preview.yaml | PR comment workflow for version bump preview. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| <div className="fixed inset-0 z-55 flex items-center justify-center bg-black/75"> | ||
| <div className="w-full max-w-md rounded-lg border border-zinc-850 bg-slate-900 p-6 shadow-xl animate-in fade-in zoom-in-95 duration-150"> | ||
| <h3 className="text-lg font-semibold text-zinc-100">Reset Template Code</h3> |
| {isLangOpen && ( | ||
| <div className="absolute left-0 mt-1.5 z-45 w-40 rounded-md border border-zinc-800 bg-slate-900 py-1 shadow-lg"> | ||
| {LANGUAGES.map((lang) => ( | ||
| <button |
| # make request | ||
| response = requests.post(exec_url, json=payload, headers=headers) | ||
|
|
||
| # get the output | ||
| output = response.json() | ||
|
|
| import axios from "axios"; | ||
| import { describe, it, expect } from "vitest"; | ||
|
|
||
| // note: this assumes both backend server and judge0 is already running and reachable | ||
| describe("Student Code", () => { | ||
| it("should execute student code", async () => { | ||
| const response = await axios.post(`${process.env.VITE_BACKEND_URL}/execute`, { | ||
| code: "print('Hello, World!')" | ||
| }); | ||
| expect(response.data).toEqual(expect.objectContaining({ | ||
| stdout: "Hello, World!\n", | ||
| stderr: null, | ||
| status: expect.objectContaining({ | ||
| id: 3, | ||
| description: "Accepted" | ||
| }) | ||
| })); | ||
| }); | ||
| }); No newline at end of file |
| className={cn( | ||
| "absolute top-1 bottom-1 w-[calc(33.333%-4px)] bg-white dark:bg-zinc-900 rounded-full shadow-md transition-all duration-300 cubic-bezier(0.2, 0, 0, 1)", | ||
| activeTab === "signin" |
| const activeQuestion = problemId | ||
| ? fetchedQuestion | ||
| : (questions && questions.length > 0 ? questions[0] : null) | ||
|
|
🚀 Release PreviewIf you merge this PR right now, Based on the conventional commits found in this branch. |
🚀 Release PreviewIf you merge this PR right now, Based on the conventional commits found in this branch. |
🚀 Release PreviewIf you merge this PR right now, Based on the conventional commits found in this branch. |
📝 WalkthroughWalkthroughThis PR establishes the initial ExemplAI monorepo containing a FastAPI/Python backend that proxies student code submissions to a Judge0 execution engine, and a TanStack Start/React frontend backed by Convex (database + backend functions), Better Auth (email/password and magic link), and an invitation-code gating system. It also adds CI/CD workflows for automated semantic releases via git-cliff. ChangesFull-Stack Application
CI/CD and Release Automation
Sequence Diagram(s)sequenceDiagram
participant Student
participant AuthPage
participant MagicLinkForm
participant checkUserExists
participant InvitationCodeModal
participant Convex
participant authClient
participant AuthenticatedLayout
participant CourseRoute
participant FastAPI
participant Judge0
rect rgba(100, 149, 237, 0.5)
Note over Student,AuthenticatedLayout: Sign-up via Magic Link with Invitation Code
Student->>AuthPage: visit /auth?redirect=/
Student->>MagicLinkForm: submit email
MagicLinkForm->>checkUserExists: POST email
checkUserExists-->>MagicLinkForm: exists: false
MagicLinkForm->>AuthPage: onUserNotFound(email)
AuthPage->>InvitationCodeModal: open
Student->>InvitationCodeModal: enter invitation code
InvitationCodeModal->>Convex: validateCode(code)
Convex-->>InvitationCodeModal: isValid: true
InvitationCodeModal->>authClient: signIn.magicLink(email, callbackURL+code)
authClient-->>Student: magic link email (dev: console log)
Student->>AuthenticatedLayout: click magic link → /_authenticated?code=XYZ
AuthenticatedLayout->>Convex: createUserAndUseCode(email, code, tokenId)
Convex-->>AuthenticatedLayout: success
AuthenticatedLayout->>AuthenticatedLayout: navigate(replace, strip code param)
end
rect rgba(100, 200, 100, 0.5)
Note over Student,Judge0: Code Execution
Student->>CourseRoute: select problem, write code, click Run
CourseRoute->>FastAPI: POST /execute { code, language_id }
FastAPI->>Judge0: POST /submissions?wait=true + X-Auth-Token
Judge0-->>FastAPI: { stdout, stderr, compile_output, status }
FastAPI-->>CourseRoute: execution result JSON
CourseRoute->>Student: Terminal drawer shows status/output
end
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
|
@coderabbitai full review |
✅ Action performedFull review finished. |
There was a problem hiding this comment.
Actionable comments posted: 16
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
🟡 Minor comments (16)
web/src/components/home/CourseList.tsx-219-232 (1)
219-232:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winUse data-driven week filters instead of a hardcoded week range.
Line 219 hardcodes weeks 1–4, but the displayed data is grouped dynamically by
sortedWeeks. If the dataset includes Week 5+, users can’t filter to those weeks via the controls. Generate these buttons from the available week list.Suggested patch
- {[1, 2, 3, 4].map((wk) => ( + {sortedWeeks.map((wk) => ( <button key={wk} onClick={() => setSelectedWeek(wk)} className={cn( "px-3 py-1.5 text-xs font-bold rounded-lg border cursor-pointer transition-colors", selectedWeek === wk ? "bg-sea-ink text-white border-sea-ink" : "bg-sand/30 border-line text-sea-ink hover:bg-sand/65" )} > Week {wk} </button> ))}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/src/components/home/CourseList.tsx` around lines 219 - 232, The week filter buttons are hardcoded to display weeks 1 through 4 using a static array in the map function, but the component dynamically groups data into sortedWeeks. Replace the hardcoded [1, 2, 3, 4] array in the map function with the sortedWeeks variable so that the buttons are generated based on the actual weeks present in the dataset, allowing users to filter to any week that has data.web/convex/courses.ts-4-12 (1)
4-12:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winMisleading function name and comment.
The function is named
getAllCoursesbut queries thequestionstable and returns questions. The comment mentions "last 100 tasks" but ascending order returns the first 100 records.Consider renaming to
getAllQuestionsorgetQuestionsByWeekand updating the comment.📝 Suggested fix
-// Return the last 100 tasks in a given task list. -export const getAllCourses = query({ +// Return up to 100 questions ordered by week (ascending). +export const getAllQuestions = query({ args: {}, handler: async (ctx, _args) => { - // take is not 100 - all const questions = await ctx.db.query("questions").withIndex("by_week").order("asc").take(100); return questions; }, });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/convex/courses.ts` around lines 4 - 12, The function `getAllCourses` has a misleading name and comment that do not match its actual implementation. The function queries the "questions" table and returns questions (not courses), and the comment incorrectly states "last 100 tasks" when the ascending order actually returns the first 100 records. Rename the function to accurately reflect what it does (either `getAllQuestions` or `getQuestionsByWeek`) and update the comment to correctly describe that it returns the first 100 questions ordered by week in ascending order..github/workflows/pr-preview.yaml.disabled-31-37 (1)
31-37:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winGuard against empty version parse before writing output.
If
git_cliff_dryfails or returns no semver match, Line 36 writescalculated_version=v, which posts misleading preview output.Suggested guard
- name: Parse Version id: version_parse run: | # git-cliff outputs text; we grab the version line VERSION=$(echo "${{ steps.git_cliff_dry.outputs.content }}" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -n 1) - echo "calculated_version=v$VERSION" >> $GITHUB_OUTPUT + if [ -z "$VERSION" ]; then + echo "calculated_version=unavailable" >> "$GITHUB_OUTPUT" + else + echo "calculated_version=v$VERSION" >> "$GITHUB_OUTPUT" + fi🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/pr-preview.yaml.disabled around lines 31 - 37, The version parsing logic in the "Parse Version" step does not validate that a version was actually found before writing the output. If git_cliff_dry fails or the grep command finds no semver match, the VERSION variable will be empty and the echo command will write an invalid calculated_version=v value. Add a guard condition after the VERSION assignment to check if VERSION is not empty before writing to GITHUB_OUTPUT, and either fail the step or provide clear error handling if no valid version is extracted from the git_cliff_dry output.server/main.py-26-33 (1)
26-33:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winCORS
allow_origins=["*"]withallow_credentials=Trueis ineffective.Browsers ignore credentials when the origin is a wildcard. If you need credentials (cookies), you must specify explicit origins. The current config gives a false sense of security while not actually enabling credentialed cross-origin requests.
Either remove
allow_credentials=Trueif you don't need cookies, or specify explicit origins in production.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@server/main.py` around lines 26 - 33, The CORSMiddleware configuration has a conflicting setup where allow_origins is set to a wildcard ["*"] while allow_credentials is set to True, which browsers will reject. Either remove allow_credentials=True from the middleware configuration if credentials are not needed for cross-origin requests, or replace the wildcard allow_origins with explicit origin URLs (such as your frontend domain) to properly support credentialed requests in production environments.web/test/studentcode.test.ts-4-18 (1)
4-18:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winTest will fail with unclear error if
VITE_BACKEND_URLis undefined.If the environment variable is not set, axios will POST to
undefined/execute, causing a confusing network error rather than a clear configuration failure. Consider validating the env var or providing a default.🛠️ Proposed fix
+const BACKEND_URL = process.env.VITE_BACKEND_URL; +if (!BACKEND_URL) { + throw new Error("VITE_BACKEND_URL environment variable is required for integration tests"); +} + describe("Student Code", () => { it("should execute student code", async () => { - const response = await axios.post(`${process.env.VITE_BACKEND_URL}/execute`, { + const response = await axios.post(`${BACKEND_URL}/execute`, { code: "print('Hello, World!')" });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/test/studentcode.test.ts` around lines 4 - 18, The test "should execute student code" directly uses process.env.VITE_BACKEND_URL without validating it exists, which will cause a confusing network error if the environment variable is undefined. Add validation at the start of the test (or in a beforeEach hook) to check that VITE_BACKEND_URL is defined and throw a clear error message if it is missing, so developers know to configure the environment variable rather than debugging a network failure.web/tsconfig.json-5-5 (1)
5-5:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winRemove backup file from TypeScript include scope.
Line 5 includes a likely stale artifact (
auth.tsx.old), which can pollute type-checking and route generation with obsolete code.Suggested fix
"include": [ "**/*.ts", "**/*.tsx", - "src/routes/auth.tsx.old" ],🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/tsconfig.json` at line 5, The TypeScript configuration file includes a stale backup file `src/routes/auth.tsx.old` in the include array, which should be removed to prevent obsolete code from polluting type-checking and route generation. Locate the include array in the TypeScript config and remove the entry for `src/routes/auth.tsx.old`.web/src/components/ide/Terminal.tsx-45-47 (1)
45-47:⚠️ Potential issue | 🟡 MinorHandle zero-memory values correctly in the memory display.
At line 45, the truthy check treats
0as missing and shows"N/A". Use a nullish check so valid zero values are rendered.Suggested fix
- {executionResult.memory + {executionResult.memory != null ? `${(executionResult.memory / 1024).toFixed(2)} MB` : "N/A"}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/src/components/ide/Terminal.tsx` around lines 45 - 47, The memory display in the Terminal.tsx component uses a truthy check on executionResult.memory which incorrectly treats a value of 0 as missing data and displays "N/A". Change the conditional check from a truthy evaluation to a nullish check (checking only for null or undefined) so that valid zero memory values are properly rendered as "0.00 MB" instead of "N/A". This ensures accurate memory reporting when the actual memory usage is zero bytes.web/src/components/student/problem/ChatBox.tsx-254-283 (1)
254-283:⚠️ Potential issue | 🟡 MinorClear pending timeout when resetting chat and on unmount.
The
setTimeoutat line 254 is not stored or cancellable. If a user clears the chat whileisTyping, the timeout will still fire after 1200ms and inject the assistant message into the reset state. Similarly, unmounting while a timeout is pending leaves an orphaned callback.Suggested fix
export default function ChatBox() { + const replyTimeoutRef = React.useRef<ReturnType<typeof setTimeout> | null>(null); const [messages, setMessages] = React.useState<Message[]>([ { id: "welcome", sender: "assistant", content: "Hi there! I am your AI learning assistant. I can help you understand the 'Two Sum' problem, offer hints, or explain algorithms without giving away the direct solution. What would you like to discuss?", timestamp: new Date(), }, ]); const [isTyping, setIsTyping] = React.useState(false); const handleSendMessage = (text: string) => { + if (replyTimeoutRef.current) { + clearTimeout(replyTimeoutRef.current); + replyTimeoutRef.current = null; + } // 1. Add User Message const userMsg: Message = { id: Math.random().toString(), sender: "user", content: text, timestamp: new Date(), }; setMessages((prev) => [...prev, userMsg]); setIsTyping(true); // 2. Simulate AI response - setTimeout(() => { + replyTimeoutRef.current = setTimeout(() => { let reply = "I'm here to help! Could you explain your current approach, or would you like a hint about a brute force solution vs. using a hash map?"; const lower = text.toLowerCase(); if (lower.includes("hint") || lower.includes("clue")) { reply = "Here's a hint: Think about how you can check if the complement (`target - nums[i]`) exists in the array as you iterate. Can we store seen numbers to find it in O(1) time?"; } else if (lower.includes("complexity") || lower.includes("o(n)")) { reply = "A brute force solution takes O(N²) time. However, using a Hash Map lets you search for the complement in O(1) average time, bringing the overall complexity down to O(N) time and O(N) space."; } else if ( lower.includes("code") || lower.includes("solution") || lower.includes("answer") ) { reply = "I can't write the final code for you, but I can guide you! Try creating a map that stores key-value pairs of `{ number: index }`. While iterating, check if `target - current_number` is already in the map."; } const assistantMsg: Message = { id: Math.random().toString(), sender: "assistant", content: reply, timestamp: new Date(), }; setMessages((prev) => [...prev, assistantMsg]); setIsTyping(false); + replyTimeoutRef.current = null; }, 1200); }; const handleClearChat = () => { + if (replyTimeoutRef.current) { + clearTimeout(replyTimeoutRef.current); + replyTimeoutRef.current = null; + } + setIsTyping(false); setMessages([ { id: "welcome-reset", sender: "assistant", content: "Chat reset. How can I help you with this problem?", timestamp: new Date(), }, ]); }; + + React.useEffect(() => { + return () => { + if (replyTimeoutRef.current) { + clearTimeout(replyTimeoutRef.current); + } + }; + }, []);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/src/components/student/problem/ChatBox.tsx` around lines 254 - 283, The setTimeout call in the message handling logic needs to be cancellable to prevent orphaned timeouts. Store the setTimeout ID in a useRef variable, then clear it in two places: first, add a useEffect cleanup function that clears the timeout on component unmount, and second, clear the timeout before resetting the chat state in the function that handles chat clearing. This ensures the pending timeout is always cancelled before it can fire and update state after unmounting or after the chat has been reset.web/src/components/student/CodeEditor.tsx-4-4 (1)
4-4:⚠️ Potential issue | 🟡 MinorReset
activeTabto "result" when new execution results arrive.Currently,
activeTabis never reset whenexecutionResultchanges. If a user views the stdout tab from a previous run and the next execution has no stdout output, the activeTab remains on "stdout" even though the tab button won't render (sincehasStdoutis false). This leaves the UI displaying an empty stdout pane with no visible way to switch back to the result view.Add a useEffect to reset the tab on new results:
Suggested fix
-import { useState } from "react"; +import { useEffect, useState } from "react"; @@ const [activeTab, setActiveTab] = useState<"result" | "stdout">("result"); + useEffect(() => { + setActiveTab("result"); + }, [executionResult]);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/src/components/student/CodeEditor.tsx` at line 4, The activeTab state is not reset when executionResult changes, causing the UI to display empty panes if a user was viewing a tab from a previous execution that has no content in the new results. Import useEffect from "react" alongside the existing useState import, then add a useEffect hook with executionResult as a dependency that resets the activeTab state back to "result" whenever new execution results arrive.web/src/routes/auth.tsx-49-55 (1)
49-55:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winTrim invitation codes before validation.
Whitespace around the submitted code currently causes avoidable validation failures.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/src/routes/auth.tsx` around lines 49 - 55, In the handleModalSubmit function, the codeToSubmit parameter is passed directly to the convex.query call for validating the invitation code without trimming whitespace. Apply the trim() method to codeToSubmit before passing it to the convex.query(api.invitationCodes.validateCode) call to remove any leading or trailing whitespace that could cause validation failures.web/src/lib/auth.functions.ts-11-11 (1)
11-11:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winValidator needs real runtime checks.
Line 11 currently passes raw input through. Add runtime validation/normalization (type, trim, format, length) before calling
findUserByEmail.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/src/lib/auth.functions.ts` at line 11, The validator function at the .validator() call is currently just passing the email parameter through without performing any actual validation or normalization. Replace the simple passthrough with proper runtime validation that includes type checking to ensure it's a string, trimming whitespace, validating the email format matches a proper email pattern, checking reasonable length constraints, and normalizing the value before it's used by findUserByEmail downstream. Return the validated and normalized email or throw an error if any validation check fails.web/src/components/auth/forms/MagicLinkForm.tsx-28-30 (1)
28-30:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winNormalize email before existence check and magic-link request.
Using raw input can misclassify users when whitespace is present.
Proposed fix
onSubmit: async ({ value }) => { try { + const email = value.email.trim() + // Check if email exists in authentication database - const { exists } = await checkUserExists({ data: value.email }); + const { exists } = await checkUserExists({ data: email }); if (!exists) { - onUserNotFound(value.email); + onUserNotFound(email); return; } @@ const { error } = await authClient.signIn.magicLink({ - email: value.email, + email, callbackURL: redirectUrl || "/", });Also applies to: 37-38
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/src/components/auth/forms/MagicLinkForm.tsx` around lines 28 - 30, The email value is being used directly without normalization in the checkUserExists function call and the subsequent magic-link request, which can cause user misclassification when whitespace is present. Normalize the email value from value.email by trimming whitespace and converting to lowercase before passing it to the checkUserExists function call at line 28, and also use this normalized email in the magic-link request at lines 37-38. Store the normalized email in a variable and use it consistently throughout this form submission logic.web/src/components/auth/AlreadySignedIn.tsx-39-45 (1)
39-45:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAdd fallbacks for optional profile fields.
nameandProposed fix
- {session.user.name} + {session.user.name ?? "there"} @@ - {session.user.email} + {session.user.email ?? "an unknown email"}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/src/components/auth/AlreadySignedIn.tsx` around lines 39 - 45, The AlreadySignedIn component renders session.user.name and session.user.email directly without handling cases where these fields might be null or undefined, even though they are marked as optional in the prop type. Add fallback values for both the name field (around line 40) and the email field (around line 44) to ensure the component handles missing profile data gracefully. Use the OR operator or conditional rendering to display a sensible default message when either field is missing.web/src/integrations/tanstack-query/root-provider.tsx-10-10 (1)
10-10:⚠️ Potential issue | 🟡 Minor
TanstackQueryProvideris unused and would renderundefinedif mounted.The component is not currently imported or mounted anywhere in the codebase. However, if it's intended for use as a provider wrapper, it must accept and render
childrento work correctly. The empty function definition will cause any mounted descendants to be dropped.Proposed fix
+import * as React from "react" import { QueryClient } from '`@tanstack/react-query`' @@ -export default function TanstackQueryProvider() {} +export default function TanstackQueryProvider({ + children, +}: { + children: React.ReactNode +}) { + return <>{children}</> +}Consider either removing this unused component or implementing it properly if React Query provider integration is planned. Currently, the
QueryClientis only used for SSR router integration viasetupRouterSsrQueryIntegration().🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/src/integrations/tanstack-query/root-provider.tsx` at line 10, The TanstackQueryProvider component is currently unused and empty, meaning it would not render any child components if mounted. Either remove the TanstackQueryProvider component entirely if it's no longer needed, or if React Query provider integration is planned, modify the TanstackQueryProvider function to accept children as a prop parameter and render those children wrapped appropriately within a QueryClientProvider component to ensure proper provider functionality.web/src/components/auth/InvitationCodeModal.tsx-27-30 (1)
27-30:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winNormalize invitation code before invoking submit.
At Line 77, emptiness is checked with
trim(), but Line 29 submits the untrimmed value. This can reject otherwise valid codes with accidental whitespace.Suggested patch
const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); - onSubmit(code); + const normalizedCode = code.trim(); + if (!normalizedCode) return; + onSubmit(normalizedCode); };Also applies to: 77-77
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/src/components/auth/InvitationCodeModal.tsx` around lines 27 - 30, The handleSubmit function submits the invitation code value without trimming whitespace, while validation at line 77 uses trim() to check for emptiness. Modify the handleSubmit function to trim the code value before passing it to the onSubmit callback to ensure consistency and allow codes with accidental surrounding whitespace to be properly validated and submitted.web/src/components/auth/AuthTextField.tsx-68-75 (1)
68-75:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAdd an explicit accessible name/state to the password toggle.
At Line 68, the toggle relies on
titleonly. Addaria-label(and ideallyaria-pressed) so assistive tech can reliably announce the control state.Suggested patch
<button type="button" onClick={() => setShowPassword((prev) => !prev)} className="p-1 rounded-full text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800 hover:text-zinc-600 dark:hover:text-zinc-200 transition-colors" title={showPassword ? "Hide password" : "Show password"} + aria-label={showPassword ? "Hide password" : "Show password"} + aria-pressed={showPassword} >🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/src/components/auth/AuthTextField.tsx` around lines 68 - 75, The password toggle button in the AuthTextField component relies only on the title attribute for accessibility, which is insufficient for assistive technology. Add aria-label and aria-pressed attributes to the button element that toggles the password visibility. The aria-label should provide a descriptive name for the button, and aria-pressed should be set to the boolean value of showPassword to properly communicate the current state to screen readers and other assistive technologies.
🧹 Nitpick comments (4)
web/src/routes/_authenticated.index.tsx (1)
14-34: ⚡ Quick winConsolidate loading ownership to avoid duplicated skeleton logic.
Line 14 introduces a
Suspensefallback, butCourseListalready renders its own loading skeleton (Line 90 inweb/src/components/home/CourseList.tsx). Keeping both paths risks UI drift and redundant maintenance. Prefer a single loading strategy (either Suspense-driven or internal conditional loading).🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/src/routes/_authenticated.index.tsx` around lines 14 - 34, The Suspense component with its fallback skeleton loading UI around the CourseList component is duplicating the loading state management that CourseList already handles internally. Remove the entire Suspense wrapper (including the fallback prop with the skeleton div structure) around CourseList and render CourseList directly, allowing it to manage its own loading skeleton as it already does at line 90 in CourseList.tsx..github/workflows/pr-preview.yaml.disabled (1)
17-17: Pin action SHAs before enabling this workflow.Lines 17, 24, and 40 use mutable version tags (
@v*). Even though this file is disabled, addressing SHA pinning before enabling prevents supply-chain risk exposure when the workflow is activated.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/pr-preview.yaml.disabled at line 17, Replace the mutable version tags for the GitHub Actions on lines 17, 24, and 40 with specific commit SHAs. For each action reference using the format `actions/OWNER/ACTION@v*`, replace the mutable tag with the full commit SHA (e.g., `actions/checkout@<40-character-commit-sha>`). This pins the actions to specific versions and prevents supply-chain risk from unexpected action updates when the workflow is eventually enabled.Source: Linters/SAST tools
server/main.py (1)
80-82: 💤 Low valueStub endpoint should be removed or documented.
This
/items/{item_id}endpoint appears to be boilerplate from FastAPI's default template. Consider removing it to avoid confusion, or add a TODO comment if it's intended for future use.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@server/main.py` around lines 80 - 82, The read_item endpoint decorated with `@app.get`("/items/{item_id}") appears to be unused boilerplate code from the FastAPI template that may confuse developers about the API's actual functionality. Either remove the entire endpoint definition including the decorator and the read_item function, or if this endpoint is planned for future implementation, add a TODO comment above the decorator explaining its intended purpose and when it should be implemented.server/model/student_code.py (1)
7-10: RemoveCodeSubmissionclass or use it consistently throughout the codebase.This model is unused—
main.pyonly importsStudentCodeand manually constructs the Judge0 payload (lines 54–57) rather than usingCodeSubmission. Additionally, the inheritedcodefield and the definedsource_codefield create semantic redundancy. Either remove the class entirely, or use it to standardize the Judge0 submission contract by importing and using it in the/executeendpoint.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@server/model/student_code.py` around lines 7 - 10, The CodeSubmission class in student_code.py is unused and creates semantic redundancy with inherited and defined fields. Either remove the CodeSubmission class entirely from student_code.py, or standardize the codebase by updating main.py to import and use CodeSubmission in the /execute endpoint instead of manually constructing the Judge0 payload. Choose one approach and ensure consistency: if keeping CodeSubmission, replace the manual payload construction (currently around lines 54-57 in main.py) with an instance of CodeSubmission, and remove the StudentCode import if it becomes unnecessary.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/release.yaml:
- Around line 16-19: Replace all mutable action version tags with pinned commit
SHAs in the release workflow. Specifically, update the actions/checkout@v4 on
line 17 and any other actions on lines 29, 37, and 46 that use mutable tags like
`@v4` or `@v2` with their full commit SHA references instead. Additionally, add
persist-credentials: false to the checkout action's with block on line 19 to
prevent credential persistence and reduce the attack surface in this release
workflow that has contents: write permission.
In @.gitignore:
- Line 3: The pattern on line 3 of .gitignore uses **/.env which only matches
the exact filename .env, allowing variants like .env.local and .env.production
to be committed accidentally. Change the pattern from **/.env to **/.env* to
match all environment files that start with .env prefix, ensuring all
environment file variants are properly ignored from version control.
In `@server/main.py`:
- Around line 45-75: In the judge0_execution function, add validation to ensure
JUDGE0_ENDPOINT is not None before concatenating it with the path string. Add a
timeout parameter to the requests.post call to prevent indefinite blocking. Wrap
the requests.post call and response.json() parsing in a try-except block to
handle network errors, HTTP errors, and JSON decoding failures, returning
appropriate error responses with meaningful error messages for each failure
case.
In `@web/biome.json`:
- Line 2: The $schema field in web/biome.json references version 2.2.4 but the
installed Biome version in web/package.json is 2.4.5, causing backwards
compatibility warnings. Update the $schema value from the versioned URL pointing
to 2.2.4 to either the matching version 2.4.5, or better yet, use a local path
reference to the configuration schema file in node_modules. The local path
approach is recommended because it automatically tracks the installed Biome
version and eliminates the need for manual updates when upgrading Biome in the
future.
In `@web/convex/invitationCodes.ts`:
- Around line 222-247: The tokenIdentifier is only being updated conditionally
in the else block when the user already exists by email. If the user has an
existing tokenIdentifier that differs from the incoming one, the condition `if
(!user.tokenIdentifier)` prevents the update, and then useCodeHelper is called
with the new tokenIdentifier that doesn't match what's stored for that user.
Remove the conditional check on `!user.tokenIdentifier` in the else block and
unconditionally update the user's tokenIdentifier to args.tokenIdentifier before
calling useCodeHelper, ensuring the lookup in useCodeHelper will succeed
regardless of whether the user previously had a tokenIdentifier stored.
In `@web/package.json`:
- Around line 21-31: The web/package.json file has multiple `@tanstack`
dependencies pinned to "latest" versions which breaks reproducibility across
different environments and CI runs. Replace all "latest" version tags with
explicit semver ranges for the following packages: `@tanstack/react-devtools`,
`@tanstack/react-query`, `@tanstack/react-query-devtools`, `@tanstack/react-router`,
`@tanstack/react-router-devtools`, `@tanstack/react-router-ssr-query`,
`@tanstack/react-start`, `@tanstack/react-store`, and `@tanstack/store`. Use the
currently locked versions from pnpm-lock.yaml as the source of truth, and format
them as explicit semver ranges (e.g., ^x.y.z) to match the pattern already used
for `@tanstack/react-form` and `@tanstack/router-plugin`.
In `@web/src/components/auth/forms/MagicLinkForm.tsx`:
- Line 34: Remove the console.log statement from the MagicLinkForm component
that logs the user's email address. The line containing the console.log with the
email value should be deleted entirely to prevent exposing PII in client logs
during the magic link sign-in process.
In `@web/src/components/auth/forms/SignUpForm.tsx`:
- Around line 35-55: The signUp process in the SignUpForm component creates the
Better Auth user account at the authClient.signUp.email call before validating
and recording the invitation code redemption in the createUserAndUseCode
function. Restructure the signup flow to validate and redeem the invitation code
before or atomically with user account creation, ensuring that if invitation
redemption fails, the auth account is not created or the user is prevented from
completing the signup process. This prevents authenticated sessions from
bypassing the invitation gate through partial failure scenarios.
In `@web/src/lib/auth.functions.ts`:
- Around line 10-16: The `checkUserExists` function creates a user enumeration
vulnerability by explicitly returning whether an email exists in the system,
allowing attackers to probe for registered accounts. Modify the handler in
`checkUserExists` to return a generic response that does not reveal whether the
user was found, regardless of the result from
`context.internalAdapter.findUserByEmail()`. Any actual user verification based
on email existence should be moved to a server-side trusted context that only
executes after proper authentication and verification.
In `@web/src/lib/auth.ts`:
- Around line 13-17: The sendMagicLink function is logging sensitive
authentication data including the magic-link URL and user email address, which
poses a security risk. Remove or replace the console.log statements that output
the email and url variables with a generic message that does not expose any
sensitive information. Ensure that only non-sensitive debug information remains
in the logs, such as a simple indication that a magic link was processed without
revealing the actual email or URL details.
- Line 6: Remove the hardcoded fallback secret string from the `secret` property
initialization in the auth configuration. Instead of using the OR operator with
a known default secret value, ensure that `process.env.BETTER_AUTH_SECRET` is
required and throw an error if the environment variable is not defined. This
will cause the application to fail fast during initialization if the proper
authentication secret is not configured, rather than silently falling back to an
insecure default.
In `@web/src/routes/__root.tsx`:
- Around line 56-67: The TanStackDevtools component is currently rendered
unconditionally for all users, exposing internal details and adding overhead in
production builds. Wrap the entire TanStackDevtools component block with a
conditional check using import.meta.env.DEV so that it only renders when the
application is running in development mode. This ensures devtools are excluded
from production builds while maintaining access for developers during
development.
In `@web/src/routes/_authenticated.course.tsx`:
- Around line 115-118: The axios.post request for code execution at the endpoint
`${url}/execute` lacks a timeout configuration, which can cause the UI to remain
in a loading state indefinitely if the backend becomes slow or unresponsive. Add
a timeout configuration to the axios.post call by passing a config object as the
third parameter that specifies a reasonable timeout value (in milliseconds) to
ensure the request fails gracefully if the backend does not respond within the
specified duration.
In `@web/src/routes/_authenticated.tsx`:
- Around line 40-63: The `.catch()` handler in the useEffect hook only logs the
error from `createUserAndUseCode` failure but does not prevent the user from
accessing protected routes. To fail closed, add error state handling in the
catch block to either set an error flag that prevents rendering protected
content, or navigate the user away from the authenticated route when the
invitation code redemption fails. This ensures users cannot proceed when the
code redemption does not complete successfully.
In `@web/src/routes/auth.tsx`:
- Around line 24-27: The validateSearch function currently accepts any string
value for the redirect parameter without validation, creating a potential open
redirect vulnerability. Modify the redirect validation to only accept safe
internal paths by implementing a check that ensures the redirect value either
starts with a forward slash (indicating a relative path) or is undefined,
rejecting any absolute external URLs. Apply the same sanitization approach to
the other locations mentioned that use or accept the redirect parameter at lines
63-64, 121, and 169-176.
In `@web/src/styles.css`:
- Around line 3-5: The `@import` 'tw-animate-css' statement must be moved to
appear before the `@plugin` '`@tailwindcss/typography`' directive. In the
web/src/styles.css file, reorder these lines so that the `@import` statement comes
first, followed by the `@plugin` statement. This ensures compliance with CSS
specification which requires `@import` declarations to precede other at-rules.
---
Minor comments:
In @.github/workflows/pr-preview.yaml.disabled:
- Around line 31-37: The version parsing logic in the "Parse Version" step does
not validate that a version was actually found before writing the output. If
git_cliff_dry fails or the grep command finds no semver match, the VERSION
variable will be empty and the echo command will write an invalid
calculated_version=v value. Add a guard condition after the VERSION assignment
to check if VERSION is not empty before writing to GITHUB_OUTPUT, and either
fail the step or provide clear error handling if no valid version is extracted
from the git_cliff_dry output.
In `@server/main.py`:
- Around line 26-33: The CORSMiddleware configuration has a conflicting setup
where allow_origins is set to a wildcard ["*"] while allow_credentials is set to
True, which browsers will reject. Either remove allow_credentials=True from the
middleware configuration if credentials are not needed for cross-origin
requests, or replace the wildcard allow_origins with explicit origin URLs (such
as your frontend domain) to properly support credentialed requests in production
environments.
In `@web/convex/courses.ts`:
- Around line 4-12: The function `getAllCourses` has a misleading name and
comment that do not match its actual implementation. The function queries the
"questions" table and returns questions (not courses), and the comment
incorrectly states "last 100 tasks" when the ascending order actually returns
the first 100 records. Rename the function to accurately reflect what it does
(either `getAllQuestions` or `getQuestionsByWeek`) and update the comment to
correctly describe that it returns the first 100 questions ordered by week in
ascending order.
In `@web/src/components/auth/AlreadySignedIn.tsx`:
- Around line 39-45: The AlreadySignedIn component renders session.user.name and
session.user.email directly without handling cases where these fields might be
null or undefined, even though they are marked as optional in the prop type. Add
fallback values for both the name field (around line 40) and the email field
(around line 44) to ensure the component handles missing profile data
gracefully. Use the OR operator or conditional rendering to display a sensible
default message when either field is missing.
In `@web/src/components/auth/AuthTextField.tsx`:
- Around line 68-75: The password toggle button in the AuthTextField component
relies only on the title attribute for accessibility, which is insufficient for
assistive technology. Add aria-label and aria-pressed attributes to the button
element that toggles the password visibility. The aria-label should provide a
descriptive name for the button, and aria-pressed should be set to the boolean
value of showPassword to properly communicate the current state to screen
readers and other assistive technologies.
In `@web/src/components/auth/forms/MagicLinkForm.tsx`:
- Around line 28-30: The email value is being used directly without
normalization in the checkUserExists function call and the subsequent magic-link
request, which can cause user misclassification when whitespace is present.
Normalize the email value from value.email by trimming whitespace and converting
to lowercase before passing it to the checkUserExists function call at line 28,
and also use this normalized email in the magic-link request at lines 37-38.
Store the normalized email in a variable and use it consistently throughout this
form submission logic.
In `@web/src/components/auth/InvitationCodeModal.tsx`:
- Around line 27-30: The handleSubmit function submits the invitation code value
without trimming whitespace, while validation at line 77 uses trim() to check
for emptiness. Modify the handleSubmit function to trim the code value before
passing it to the onSubmit callback to ensure consistency and allow codes with
accidental surrounding whitespace to be properly validated and submitted.
In `@web/src/components/home/CourseList.tsx`:
- Around line 219-232: The week filter buttons are hardcoded to display weeks 1
through 4 using a static array in the map function, but the component
dynamically groups data into sortedWeeks. Replace the hardcoded [1, 2, 3, 4]
array in the map function with the sortedWeeks variable so that the buttons are
generated based on the actual weeks present in the dataset, allowing users to
filter to any week that has data.
In `@web/src/components/ide/Terminal.tsx`:
- Around line 45-47: The memory display in the Terminal.tsx component uses a
truthy check on executionResult.memory which incorrectly treats a value of 0 as
missing data and displays "N/A". Change the conditional check from a truthy
evaluation to a nullish check (checking only for null or undefined) so that
valid zero memory values are properly rendered as "0.00 MB" instead of "N/A".
This ensures accurate memory reporting when the actual memory usage is zero
bytes.
In `@web/src/components/student/CodeEditor.tsx`:
- Line 4: The activeTab state is not reset when executionResult changes, causing
the UI to display empty panes if a user was viewing a tab from a previous
execution that has no content in the new results. Import useEffect from "react"
alongside the existing useState import, then add a useEffect hook with
executionResult as a dependency that resets the activeTab state back to "result"
whenever new execution results arrive.
In `@web/src/components/student/problem/ChatBox.tsx`:
- Around line 254-283: The setTimeout call in the message handling logic needs
to be cancellable to prevent orphaned timeouts. Store the setTimeout ID in a
useRef variable, then clear it in two places: first, add a useEffect cleanup
function that clears the timeout on component unmount, and second, clear the
timeout before resetting the chat state in the function that handles chat
clearing. This ensures the pending timeout is always cancelled before it can
fire and update state after unmounting or after the chat has been reset.
In `@web/src/integrations/tanstack-query/root-provider.tsx`:
- Line 10: The TanstackQueryProvider component is currently unused and empty,
meaning it would not render any child components if mounted. Either remove the
TanstackQueryProvider component entirely if it's no longer needed, or if React
Query provider integration is planned, modify the TanstackQueryProvider function
to accept children as a prop parameter and render those children wrapped
appropriately within a QueryClientProvider component to ensure proper provider
functionality.
In `@web/src/lib/auth.functions.ts`:
- Line 11: The validator function at the .validator() call is currently just
passing the email parameter through without performing any actual validation or
normalization. Replace the simple passthrough with proper runtime validation
that includes type checking to ensure it's a string, trimming whitespace,
validating the email format matches a proper email pattern, checking reasonable
length constraints, and normalizing the value before it's used by
findUserByEmail downstream. Return the validated and normalized email or throw
an error if any validation check fails.
In `@web/src/routes/auth.tsx`:
- Around line 49-55: In the handleModalSubmit function, the codeToSubmit
parameter is passed directly to the convex.query call for validating the
invitation code without trimming whitespace. Apply the trim() method to
codeToSubmit before passing it to the
convex.query(api.invitationCodes.validateCode) call to remove any leading or
trailing whitespace that could cause validation failures.
In `@web/test/studentcode.test.ts`:
- Around line 4-18: The test "should execute student code" directly uses
process.env.VITE_BACKEND_URL without validating it exists, which will cause a
confusing network error if the environment variable is undefined. Add validation
at the start of the test (or in a beforeEach hook) to check that
VITE_BACKEND_URL is defined and throw a clear error message if it is missing, so
developers know to configure the environment variable rather than debugging a
network failure.
In `@web/tsconfig.json`:
- Line 5: The TypeScript configuration file includes a stale backup file
`src/routes/auth.tsx.old` in the include array, which should be removed to
prevent obsolete code from polluting type-checking and route generation. Locate
the include array in the TypeScript config and remove the entry for
`src/routes/auth.tsx.old`.
---
Nitpick comments:
In @.github/workflows/pr-preview.yaml.disabled:
- Line 17: Replace the mutable version tags for the GitHub Actions on lines 17,
24, and 40 with specific commit SHAs. For each action reference using the format
`actions/OWNER/ACTION@v*`, replace the mutable tag with the full commit SHA
(e.g., `actions/checkout@<40-character-commit-sha>`). This pins the actions to
specific versions and prevents supply-chain risk from unexpected action updates
when the workflow is eventually enabled.
In `@server/main.py`:
- Around line 80-82: The read_item endpoint decorated with
`@app.get`("/items/{item_id}") appears to be unused boilerplate code from the
FastAPI template that may confuse developers about the API's actual
functionality. Either remove the entire endpoint definition including the
decorator and the read_item function, or if this endpoint is planned for future
implementation, add a TODO comment above the decorator explaining its intended
purpose and when it should be implemented.
In `@server/model/student_code.py`:
- Around line 7-10: The CodeSubmission class in student_code.py is unused and
creates semantic redundancy with inherited and defined fields. Either remove the
CodeSubmission class entirely from student_code.py, or standardize the codebase
by updating main.py to import and use CodeSubmission in the /execute endpoint
instead of manually constructing the Judge0 payload. Choose one approach and
ensure consistency: if keeping CodeSubmission, replace the manual payload
construction (currently around lines 54-57 in main.py) with an instance of
CodeSubmission, and remove the StudentCode import if it becomes unnecessary.
In `@web/src/routes/_authenticated.index.tsx`:
- Around line 14-34: The Suspense component with its fallback skeleton loading
UI around the CourseList component is duplicating the loading state management
that CourseList already handles internally. Remove the entire Suspense wrapper
(including the fallback prop with the skeleton div structure) around CourseList
and render CourseList directly, allowing it to manage its own loading skeleton
as it already does at line 90 in CourseList.tsx.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: d4229f41-6f5e-4679-93a5-3bba08553240
⛔ Files ignored due to path filters (5)
server/uv.lockis excluded by!**/*.lockweb/pnpm-lock.yamlis excluded by!**/pnpm-lock.yamlweb/public/favicon.icois excluded by!**/*.icoweb/public/logo192.pngis excluded by!**/*.pngweb/public/logo512.pngis excluded by!**/*.png
📒 Files selected for processing (65)
.github/workflows/pr-preview.yaml.disabled.github/workflows/release.yaml.gitignorecliff.tomlserver/.env.exampleserver/.python-versionserver/README.mdserver/main.pyserver/model/student_code.pyserver/pyproject.tomlweb/.cta.jsonweb/.cursorrulesweb/.env.exampleweb/.gitignoreweb/.prettierrcweb/.vscode/settings.jsonweb/README.mdweb/biome.jsonweb/components.jsonweb/convex/courses.tsweb/convex/invitationCodes.tsweb/convex/schema.tsweb/package.jsonweb/pnpm-workspace.yamlweb/public/manifest.jsonweb/public/robots.txtweb/src/components/auth/AlreadySignedIn.tsxweb/src/components/auth/AuthButton.tsxweb/src/components/auth/AuthCard.tsxweb/src/components/auth/AuthTabs.tsxweb/src/components/auth/AuthTextField.tsxweb/src/components/auth/InvitationCodeModal.tsxweb/src/components/auth/forms/MagicLinkForm.tsxweb/src/components/auth/forms/SignInForm.tsxweb/src/components/auth/forms/SignUpForm.tsxweb/src/components/home/CourseList.tsxweb/src/components/home/SidePane.tsxweb/src/components/ide/Terminal.tsxweb/src/components/student/CodeEditor.tsxweb/src/components/student/InteractionBar.tsxweb/src/components/student/ResetCodeForm.tsxweb/src/components/student/SidePane.tsxweb/src/components/student/problem/ChatBox.tsxweb/src/components/student/problem/Problem.tsxweb/src/components/ui/button.tsxweb/src/integrations/better-auth/header-user.tsxweb/src/integrations/tanstack-query/devtools.tsxweb/src/integrations/tanstack-query/root-provider.tsxweb/src/lib/auth-client.tsweb/src/lib/auth.functions.tsweb/src/lib/auth.tsweb/src/lib/utils.tsweb/src/routeTree.gen.tsweb/src/router.tsxweb/src/routes/__root.tsxweb/src/routes/_authenticated.course.tsxweb/src/routes/_authenticated.index.tsxweb/src/routes/_authenticated.tsxweb/src/routes/api/auth/$.tsweb/src/routes/auth.tsxweb/src/styles.cssweb/test/studentcode.test.tsweb/tsconfig.jsonweb/tsr.config.jsonweb/vite.config.ts
| - name: Checkout Code | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| fetch-depth: 0 # Fetches all history so git-cliff can read past commits |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify all workflow action references are SHA-pinned and checkout disables credential persistence.
fd -t f '.ya?ml$' .github/workflows -x sh -c '
f="$1"
echo "== $f =="
rg -n "uses:\s*[^@]+@([A-Za-z0-9._/-]+)$" "$f"
rg -n "uses:\s*actions/checkout@" "$f" -A4 -B1
' sh {}Repository: RMIT-BDSL/ExemplAI
Length of output: 471
🏁 Script executed:
cat -n .github/workflows/release.yamlRepository: RMIT-BDSL/ExemplAI
Length of output: 2117
Pin action SHAs and disable checkout credential persistence.
Lines 17, 29, 37, and 46 use mutable action tags (@v4, @v2), and line 19 omits persist-credentials: false. In a release workflow with contents: write permission, this creates a supply-chain attack surface and increases the blast radius of a potential token compromise.
Suggested hardening diff
- name: Checkout Code
- uses: actions/checkout@v4
+ uses: actions/checkout@<PINNED_SHA>
with:
fetch-depth: 0 # Fetches all history so git-cliff can read past commits
+ persist-credentials: false
- name: Determine Next Version
id: next_version
- uses: orhun/git-cliff-action@v4
+ uses: orhun/git-cliff-action@<PINNED_SHA>
with:
config: cliff.toml
args: --bump --bump-deps
- name: Generate Changelog
id: git_cliff
- uses: orhun/git-cliff-action@v4
+ uses: orhun/git-cliff-action@<PINNED_SHA>
with:
config: cliff.toml
args: --verbose --bump --strip all
- name: Create GitHub Release
- uses: softprops/action-gh-release@v2
+ uses: softprops/action-gh-release@<PINNED_SHA>🧰 Tools
🪛 zizmor (1.25.2)
[warning] 16-19: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
[error] 17-17: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/release.yaml around lines 16 - 19, Replace all mutable
action version tags with pinned commit SHAs in the release workflow.
Specifically, update the actions/checkout@v4 on line 17 and any other actions on
lines 29, 37, and 46 that use mutable tags like `@v4` or `@v2` with their full
commit SHA references instead. Additionally, add persist-credentials: false to
the checkout action's with block on line 19 to prevent credential persistence
and reduce the attack surface in this release workflow that has contents: write
permission.
Source: Linters/SAST tools
| @@ -1 +1,4 @@ | |||
| data/csedm-2019/ | |||
| **/.venv/ | |||
| **/.env | |||
There was a problem hiding this comment.
Ignore all environment-file variants, not only .env.
Line 3 only ignores the exact .env filename. Files like .env.local / .env.production can still be committed with secrets.
Suggested fix
**/.env
+**/.env.*
+!**/.env.example📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| **/.env | |
| **/.env | |
| **/.env.* | |
| !**/.env.example |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.gitignore at line 3, The pattern on line 3 of .gitignore uses **/.env which
only matches the exact filename .env, allowing variants like .env.local and
.env.production to be committed accidentally. Change the pattern from **/.env to
**/.env* to match all environment files that start with .env prefix, ensuring
all environment file variants are properly ignored from version control.
| @app.post('/execute') | ||
| def judge0_execution(student_code: StudentCode): | ||
| # print student code | ||
|
|
||
| # send the code to judge0 | ||
| # make new request to configured judge0 endpoint - current would block until done | ||
| exec_url = os.getenv('JUDGE0_ENDPOINT') + '/submissions?base64_encoded=false&wait=true' | ||
|
|
||
| # beautifully format the request payload | ||
| payload = { | ||
| 'source_code': student_code.code, | ||
| 'language_id': 71, # python | ||
| } | ||
|
|
||
| # setup headers if auth key is provided | ||
| headers = {} | ||
| auth_key = os.getenv('JUDGE0_AUTH_KEY') | ||
| if auth_key: | ||
| headers['X-Auth-Token'] = auth_key | ||
|
|
||
| # make request | ||
| response = requests.post(exec_url, json=payload, headers=headers) | ||
|
|
||
| # get the output | ||
| output = response.json() | ||
|
|
||
| # print the output using rich | ||
| log.info(output) | ||
|
|
||
| # return the output | ||
| return output |
There was a problem hiding this comment.
Missing timeout, error handling, and env validation on Judge0 proxy.
Several issues in this endpoint:
- No timeout —
requests.postcan hang indefinitely if Judge0 is unresponsive, blocking the request thread. - No validation of
JUDGE0_ENDPOINT— If the env var is missing,None + '/submissions...'raisesTypeError. - No error handling — Network failures, non-2xx responses, or non-JSON bodies will crash the endpoint.
🛠️ Proposed fix
+from fastapi import FastAPI, HTTPException
+
+JUDGE0_ENDPOINT = os.getenv('JUDGE0_ENDPOINT')
+if not JUDGE0_ENDPOINT:
+ raise RuntimeError("JUDGE0_ENDPOINT environment variable is required")
+
`@app.post`('/execute')
def judge0_execution(student_code: StudentCode):
- exec_url = os.getenv('JUDGE0_ENDPOINT') + '/submissions?base64_encoded=false&wait=true'
+ exec_url = JUDGE0_ENDPOINT + '/submissions?base64_encoded=false&wait=true'
payload = {
'source_code': student_code.code,
'language_id': 71,
}
headers = {}
auth_key = os.getenv('JUDGE0_AUTH_KEY')
if auth_key:
headers['X-Auth-Token'] = auth_key
- response = requests.post(exec_url, json=payload, headers=headers)
-
- output = response.json()
+ try:
+ response = requests.post(exec_url, json=payload, headers=headers, timeout=30)
+ response.raise_for_status()
+ output = response.json()
+ except requests.exceptions.Timeout:
+ raise HTTPException(status_code=504, detail="Judge0 request timed out")
+ except requests.exceptions.RequestException as e:
+ log.error(f"Judge0 request failed: {e}")
+ raise HTTPException(status_code=502, detail="Failed to reach code execution service")
+ except ValueError:
+ raise HTTPException(status_code=502, detail="Invalid response from code execution service")
log.info(output)
return output🧰 Tools
🪛 ast-grep (0.43.0)
[info] 65-65: no timeout was given on call to external resource
Context: requests.post(exec_url, json=payload, headers=headers)
Note: [CWE-1088].
(requests-timeout)
🪛 Ruff (0.15.17)
[error] 66-66: Probable use of requests call without timeout
(S113)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@server/main.py` around lines 45 - 75, In the judge0_execution function, add
validation to ensure JUDGE0_ENDPOINT is not None before concatenating it with
the path string. Add a timeout parameter to the requests.post call to prevent
indefinite blocking. Wrap the requests.post call and response.json() parsing in
a try-except block to handle network errors, HTTP errors, and JSON decoding
failures, returning appropriate error responses with meaningful error messages
for each failure case.
Source: Linters/SAST tools
| @@ -0,0 +1,44 @@ | |||
| { | |||
| "$schema": "https://biomejs.dev/schemas/2.2.4/schema.json", | |||
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's check the actual content of web/biome.json
cat -n web/biome.json | head -10Repository: RMIT-BDSL/ExemplAI
Length of output: 331
🏁 Script executed:
# Check package.json for Biome version
cat -n package.json | grep -A 2 -B 2 "`@biomejs/biome`"Repository: RMIT-BDSL/ExemplAI
Length of output: 107
🏁 Script executed:
# Also search for package-lock.json or similar to verify actual installed version
fd -e "package-lock.json|yarn.lock|pnpm-lock.yaml" | head -5Repository: RMIT-BDSL/ExemplAI
Length of output: 44
🏁 Script executed:
# Let's search for package.json in the entire repository
fd -name "package.json" -type fRepository: RMIT-BDSL/ExemplAI
Length of output: 293
🏁 Script executed:
# Let's also check for any biome references in the repository
rg "biomejs" --type json -lRepository: RMIT-BDSL/ExemplAI
Length of output: 105
🏁 Script executed:
# Check what files exist in the web directory
ls -la web/ | head -20Repository: RMIT-BDSL/ExemplAI
Length of output: 1235
🏁 Script executed:
# Check the web/package.json for Biome version
cat web/package.jsonRepository: RMIT-BDSL/ExemplAI
Length of output: 2100
🏁 Script executed:
# Also check the root package.json for comparison
cat package.json | grep -A 5 -B 5 "biomejs\|biome"Repository: RMIT-BDSL/ExemplAI
Length of output: 107
🏁 Script executed:
# Let's also check the root biome.json to see if there's a different configuration
cat biome.jsonRepository: RMIT-BDSL/ExemplAI
Length of output: 1320
🌐 Web query:
Biome schema compatibility 2.2.4 vs 2.4.5 version mismatch
💡 Result:
When upgrading Biome from version 2.2.4 to 2.4.5, you will likely encounter a warning or error regarding a configuration schema mismatch [1][2][3]. Biome includes a built-in mechanism to detect when the $schema field in your biome.json file does not match the version of the installed CLI [1][3][4]. To resolve this mismatch, you should run the following command in your project root: biome migrate --write This command automatically updates your biome.json file, including the $schema field, to ensure compatibility with your current Biome version [1][5]. Recommendation: To avoid frequent manual updates to the $schema URL when you bump Biome versions, you can point to the locally installed schema file instead of a specific versioned URL [2]. You can replace the URL in your biome.json with the following path: "$schema": "./node_modules/@biomejs/biome/configuration_schema.json" Using this local path allows Biome to automatically use the schema associated with your currently installed package version, reducing the need to run migration commands solely for schema version synchronization [2].
Citations:
- 1: 📎 Report outdated
$schemain the Biome configuration file biomejs/biome#4147 - 2: 🐛 Point to locally installed schema definition by default biomejs/biome#7911
- 3: Error on mismatched `$schema` version biomejs/biome#9110
- 4: feat(biome_cli): add schema version check to
check,lint,formatcommands biomejs/biome#4796 - 5: https://biomejs.dev/guides/upgrade-to-biome-v2/
🌐 Web query:
Biome biomejs.dev schemas backwards compatibility newer versions
💡 Result:
Biome does not strictly maintain backwards compatibility for the $schema field in its configuration files [1][2]. The tool expects the $schema version defined in your biome.json or biome.jsonc file to match the version of the Biome CLI you are currently using [1][3][4]. When there is a mismatch—specifically if the schema version is older than the CLI version—the Biome CLI will emit a warning diagnostic [1][4][5]. To resolve this and ensure your configuration remains valid and up-to-date with your current CLI version, you should run the following command [1][2]: biome migrate This command is designed to update your configuration file and synchronize the $schema version [1][2]. For managing this in projects, you have two primary options for specifying the $schema: 1. Remote URL: You can use the versioned URL (e.g., https://biomejs.dev/schemas/2.4.13/schema.json) [6][7]. If you use this approach, you must update the version number whenever you upgrade the Biome package to avoid version mismatch warnings [1][8]. 2. Local Path: You can reference the schema bundled within your project's node_modules [7]. This is often preferred because it automatically tracks the version of the Biome package installed in your project, reducing the need to manually update the URL [1][7]. You can set it like this: { "$schema": "./node_modules/@biomejs/biome/configuration_schema.json" } Because Biome is a rapidly evolving toolchain, the maintainers advise against ignoring these warnings, as even minor or patch updates can introduce changes to lint rules or formatting behaviors [8][9]. Using the biome migrate command or a local path reference helps ensure your configuration remains compatible with your specific installed version [1][2][7].
Citations:
- 1: 📎 Report outdated
$schemain the Biome configuration file biomejs/biome#4147 - 2: 📎 Make install script that keeps biome.json $schema version in sync with package version biomejs/biome#3622
- 3: 🐛
npx @biomejs/biome@beta migratedoesn't update the$schemafield biomejs/biome#5430 - 4: 🐛 schema.json missing for 2.0.1 and 2.0.2 biomejs/biome#6448
- 5: 🐛 Biome emits several times the same message for outdated $schema biomejs/biome#5817
- 6: https://biomejs.dev/guides/configure-biome/
- 7: https://github.com/biomejs/website/blob/main/src/content/docs/reference/configuration.mdx
- 8: Ability to disable schema version check biomejs/biome#7376
- 9: https://biomejs.dev/internals/versioning/
Update Biome schema version in web/biome.json to match the installed Biome version.
The schema URL in web/biome.json references version 2.2.4, but web/package.json specifies @biomejs/biome@2.4.5. Biome does not maintain backwards compatibility for schema versions and will emit a warning diagnostic when the schema version is older than the installed CLI version. Update the schema to 2.4.5 to match the installed version.
For a more robust long-term solution, consider using a local path reference instead of a versioned URL:
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json"This approach automatically tracks the installed Biome version and eliminates the need for manual schema URL updates during version upgrades.
🔧 Quick fix
- "$schema": "https://biomejs.dev/schemas/2.2.4/schema.json",
+ "$schema": "https://biomejs.dev/schemas/2.4.5/schema.json",🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@web/biome.json` at line 2, The $schema field in web/biome.json references
version 2.2.4 but the installed Biome version in web/package.json is 2.4.5,
causing backwards compatibility warnings. Update the $schema value from the
versioned URL pointing to 2.2.4 to either the matching version 2.4.5, or better
yet, use a local path reference to the configuration schema file in
node_modules. The local path approach is recommended because it automatically
tracks the installed Biome version and eliminates the need for manual updates
when upgrading Biome in the future.
| try { | ||
| const response = await axios.post(`${url}/execute`, { | ||
| code: submissionCode, | ||
| }) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify axios calls and whether they include explicit timeout options.
rg -nP --type=ts --type=tsx 'axios\.(get|post|put|patch|delete)\(' web/src -C2
rg -nP --type=ts --type=tsx 'timeout\s*:' web/src -C2Repository: RMIT-BDSL/ExemplAI
Length of output: 118
🏁 Script executed:
#!/bin/bash
# Read the specific file to verify the axios call at lines 115-118
if [ -f "web/src/routes/_authenticated.course.tsx" ]; then
echo "=== File content at lines 110-125 ==="
sed -n '110,125p' web/src/routes/_authenticated.course.tsx
else
echo "File not found at expected path"
find . -name "*authenticated.course*" -type f
fiRepository: RMIT-BDSL/ExemplAI
Length of output: 720
🏁 Script executed:
#!/bin/bash
# Search for axios calls without problematic type flags
echo "=== All axios calls in web/src ==="
rg 'axios\.(get|post|put|patch|delete)\(' web/src -A2 -B1
echo -e "\n=== Timeout configurations in web/src ==="
rg 'timeout\s*:' web/src -B2 -A1Repository: RMIT-BDSL/ExemplAI
Length of output: 437
Add an explicit timeout for code execution requests.
The execution request at Line 116 lacks a timeout configuration. A slow or hung backend can leave the UI in a prolonged loading state, degrading route responsiveness.
Suggested fix
const response = await axios.post(`${url}/execute`, {
code: submissionCode,
- })
+ }, {
+ timeout: 30000,
+ })📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| try { | |
| const response = await axios.post(`${url}/execute`, { | |
| code: submissionCode, | |
| }) | |
| try { | |
| const response = await axios.post(`${url}/execute`, { | |
| code: submissionCode, | |
| }, { | |
| timeout: 30000, | |
| }) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@web/src/routes/_authenticated.course.tsx` around lines 115 - 118, The
axios.post request for code execution at the endpoint `${url}/execute` lacks a
timeout configuration, which can cause the UI to remain in a loading state
indefinitely if the backend becomes slow or unresponsive. Add a timeout
configuration to the axios.post call by passing a config object as the third
parameter that specifies a reasonable timeout value (in milliseconds) to ensure
the request fails gracefully if the backend does not respond within the
specified duration.
| useEffect(() => { | ||
| if (session?.user && code) { | ||
| createUserAndUseCode({ | ||
| email: session.user.email, | ||
| name: session.user.name || undefined, | ||
| image: session.user.image || undefined, | ||
| code: code, | ||
| tokenIdentifier: session.user.id, | ||
| }) | ||
| .then(() => { | ||
| console.log("Successfully synced user to Convex and redeemed invitation code."); | ||
| // Remove the code parameter from the search query after success | ||
| navigate({ | ||
| search: (prev: any) => { | ||
| const { code: _, ...rest } = prev; | ||
| return rest; | ||
| }, | ||
| replace: true, | ||
| }); | ||
| }) | ||
| .catch((err) => { | ||
| console.error("Failed to sync user and redeem code in Convex:", err); | ||
| }); | ||
| } |
There was a problem hiding this comment.
Fail closed when invitation-code sync fails.
On failure, the code only logs an error and still renders protected routes. That lets authenticated users proceed even when invitation redemption did not complete.
Suggested direction
function AuthenticatedLayout() {
+ const [inviteSyncFailed, setInviteSyncFailed] = useState(false)
const { session } = Route.useRouteContext();
const { code } = Route.useSearch();
const navigate = useNavigate();
const createUserAndUseCode = useMutation(api.invitationCodes.createUserAndUseCode);
useEffect(() => {
if (session?.user && code) {
createUserAndUseCode({
email: session.user.email,
name: session.user.name || undefined,
image: session.user.image || undefined,
code: code,
tokenIdentifier: session.user.id,
})
.then(() => {
navigate({
search: (prev: any) => {
const { code: _, ...rest } = prev;
return rest;
},
replace: true,
});
})
.catch((err) => {
- console.error("Failed to sync user and redeem code in Convex:", err);
+ setInviteSyncFailed(true)
});
}
}, [session, code, createUserAndUseCode, navigate]);
+
+ if (code && inviteSyncFailed) {
+ return null // or redirect to /auth with an explicit error state
+ }
return <Outlet />;
}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@web/src/routes/_authenticated.tsx` around lines 40 - 63, The `.catch()`
handler in the useEffect hook only logs the error from `createUserAndUseCode`
failure but does not prevent the user from accessing protected routes. To fail
closed, add error state handling in the catch block to either set an error flag
that prevents rendering protected content, or navigate the user away from the
authenticated route when the invitation code redemption fails. This ensures
users cannot proceed when the code redemption does not complete successfully.
| @plugin '@tailwindcss/typography'; | ||
|
|
||
| @import 'tw-animate-css'; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
python - <<'PY'
from pathlib import Path
p = Path("web/src/styles.css")
lines = p.read_text().splitlines()
first_non_import = None
late_imports = []
for i, raw in enumerate(lines, 1):
s = raw.strip()
if not s or s.startswith("/*"):
continue
if s.startswith("`@import`"):
if first_non_import is not None:
late_imports.append(i)
elif s.startswith("`@charset`") or s.startswith("`@layer`"):
continue
else:
if first_non_import is None:
first_non_import = i
print({"first_non_import_line": first_non_import, "late_import_lines": late_imports})
PYRepository: RMIT-BDSL/ExemplAI
Length of output: 117
Move @import 'tw-animate-css' above non-import at-rules.
@import must be declared before other non-@charset/non-@layer at-rules. Currently, line 5 places it after the @plugin at-rule on line 3, which violates CSS specification and triggers lint errors.
Suggested fix
`@import` url('https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,500;9..144,700&family=Manrope:wght@400;500;600;700;800&display=swap');
`@import` 'tailwindcss';
+@import 'tw-animate-css';
`@plugin` '`@tailwindcss/typography`';
-
-@import 'tw-animate-css';
`@custom-variant` dark (&:is(.dark *));🧰 Tools
🪛 Stylelint (17.13.0)
[error] 5-5: Invalid position for @import rule (no-invalid-position-at-import-rule)
(no-invalid-position-at-import-rule)
[error] 3-3: Unexpected unknown at-rule "@plugin" (scss/at-rule-no-unknown)
(scss/at-rule-no-unknown)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@web/src/styles.css` around lines 3 - 5, The `@import` 'tw-animate-css'
statement must be moved to appear before the `@plugin` '`@tailwindcss/typography`'
directive. In the web/src/styles.css file, reorder these lines so that the
`@import` statement comes first, followed by the `@plugin` statement. This ensures
compliance with CSS specification which requires `@import` declarations to precede
other at-rules.
Source: Linters/SAST tools
It is not perfect, but these should give us the initial implementation of the website to enable collaboration.
View documentation at https://exemplai-doc.pages.dev/, GitHub repository https://github.com/SocratesCodes/exemplAI-doc.
what's implemented on high level:
what is still missing
THose would be addressed in the next iteration
Summary by CodeRabbit
New Features
Tests
Chores